package com.bawei.common.event

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.lang.NullPointerException
import kotlin.jvm.Throws

/**
 * 自定义LiveData事件总线
 */

object LiveDataBus {

    private val bus: MutableMap<String, BusMutableLiveData<Any>> = mutableMapOf()

    fun <T> with(key: String): BusMutableLiveData<T> {
        if (!bus.containsKey(key)){
            bus[key] = BusMutableLiveData()
        }
        return bus[key] as BusMutableLiveData<T>
    }

    /**
     * 自定义LiveData
     */
    class BusMutableLiveData<T> : MutableLiveData<T>() {

        private val observerMap: MutableMap<Observer<*>, Observer<*>> = mutableMapOf()


        /**
         * 订阅前台事件
         */
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            super.observe(owner, observer)
            try {
                hook(observer)
            } catch (e: Exception){
                e.printStackTrace()
            }
        }

        /**
         * 订阅前台粘性事件
         */
        fun observeSticky(owner: LifecycleOwner, observer: (T) -> Unit){
            super.observe(owner, observer)
        }

        /**
         * 订阅后台事件
         */
        override fun observeForever(observer: Observer<in T>) {
            if (!observerMap.containsKey(observer)){
                observerMap[observer] = ObserverWrapper(observer)
            }
            super.observeForever(observerMap[observer] as Observer<in T>)
        }

        /**
         * 订阅后台粘性事件
         */
        fun observeForeverSticky(observer: Observer<in T>){
            super.observeForever(observer)
        }

        /**
         * 取消事件订阅
         */
        override fun removeObserver(observer: Observer<in T>) {
            val realObserver: Observer<in T> = if (observerMap.containsKey(observer)){
                observerMap.remove(observer) as Observer<in T>
            } else {
                observer
            }
            super.removeObserver(realObserver)
        }

        /**
         * 使用反射技术，修改Livedata中版本号
         */
        @Throws(Exception::class)
        private fun hook(observer: Observer<*>){
            val liveDataClass = LiveData::class.java
            //获取订阅版本号mVersion
            val mVersionField = liveDataClass.getDeclaredField("mVersion")
            mVersionField.isAccessible = true
            val version = mVersionField[this]
            //获取LiveData中mObservers集合属性
            val mObserversField = liveDataClass.getDeclaredField("mObservers")
            mObserversField.isAccessible = true
            val observers = mObserversField[this]
            val observersClass: Class<*> = observers.javaClass
            //获取集合对象的Get方法
            val methodGet = observersClass.getDeclaredMethod("get", Any::class.java)
            methodGet.isAccessible = true
            //获取对应集合中实体对象
            val subObserverEntity = methodGet.invoke(observers, observer)
            var subObserverWrapper: Any? = null
            if (subObserverEntity is Map.Entry<*, *>){
                subObserverWrapper = subObserverEntity.value
            }
            if (subObserverWrapper == null) {
                throw NullPointerException("Wrapper can not is null!!")
            }
            //获取观察者父类ObserverWrapper
            val observerWrapperClass: Class<*> ? = subObserverWrapper.javaClass.superclass
            val lastVersionField = observerWrapperClass!!.getDeclaredField("mLastVersion")
            lastVersionField.isAccessible = true
            //修改lastVersion
            lastVersionField[subObserverWrapper] = version
        }

    }


    /**
     * 自定义观察者对象
     */
    private class ObserverWrapper<T>(val observer: Observer<T>) : Observer<T>{

        override fun onChanged(value: T) {
            if (isCallOnObserverForever()) {
                return
            }
            observer.onChanged(value)
        }

        private fun isCallOnObserverForever(): Boolean{
            val stackTrace = Thread.currentThread().stackTrace
            for (item in stackTrace){
                if ("androidx.lifecycle.LiveData" == item.className && "observeForever" == item.methodName){
                    return true
                }
            }
            return false
        }

    }











}